/**
 * @author Cheng Liu
 **/

#include "stdafx.h"
#include "PluginManagerBase.h"
#include <memory>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

PluginManagerBase::PluginManagerBase() {
	WriteLog("PluginManagerBase %d Created\n",this);
}

PluginManagerBase::~PluginManagerBase() {
	if (!plugins.empty()) {
		WriteLog("Undestroyed before delete a PluginManager.\n");
		Destroy();
	}
	WriteLog("PluginManagerBase %d Destroyed\n",this);
}

HMODULE PluginManagerBase::LoadModule( const TCHAR* FileName ) {
	typedef const char * (__cdecl *stringGetter) ();
	typedef int (__cdecl *intGetter) ();

	//std::auto_ptr<PluginInfo> moduleInfo(new PluginInfo());
	PluginInfo* moduleInfo = new PluginInfo;
	HMODULE ModuleHandle = LoadLibrary(FileName);
	if (ModuleHandle == NULL) {
		WriteLog("Unable to LoadLibrary(%s)", FileName);
		return NULL;
	}

	moduleInfo->ModuleHANDLE = ModuleHandle;
	stringGetter GetName = (stringGetter)GetProcAddress(ModuleHandle, "GetPluginName");
	if (GetName == NULL) {
		WriteLog("Can't get Plugin's Name");
		return false;
	}

	moduleInfo->PluginName = GetName();

	intGetter GetVersion = (intGetter)GetProcAddress(ModuleHandle, "GetPluginVersion");
	if (GetVersion == NULL) {
		WriteLog("Can't get Plugin's Version");
		return false;
	}
	moduleInfo->PluginVersion = GetVersion();

	if (GetProcAddress(ModuleHandle, "CreatePluginObject") == NULL) {
		WriteLog("Can't get function 'CreatePluginObject'");
		return false;
	}
	
	if (GetProcAddress(ModuleHandle, "DestroyPluginObject") == NULL) {
		WriteLog("Can't get function 'DestroyPluginObject'");
		return false;
	}
	
	modules[moduleInfo->ModuleHANDLE] = moduleInfo;
	//modules[moduleInfo->ModuleHANDLE] = moduleInfo.get();
	//moduleInfo.release();
	
	return ModuleHandle;
}

bool PluginManagerBase::ReleaseModule( HMODULE module ) {

	moduleTable::iterator itModule;
	itModule = modules.find(module);
	//Not Find
	if (itModule == modules.end()) return false;

	//Release Objects
	pluginTable::iterator now,itPlugin;
	for (itPlugin = plugins.begin(); itPlugin!=plugins.end();) {
		now = itPlugin++;
		if (now->second == itModule->second) {
			DestroyPluginObject(now->first);
		}
	}

	//Release the Module Handle
	FreeLibrary(itModule->second->ModuleHANDLE);

	//Remove Module Information
	delete itModule->second;
	modules.erase(itModule);
	
	return true;
}

IPlugin* PluginManagerBase::CreatePluginObject( HMODULE module ) {
	moduleTable::iterator itModule = modules.find(module);
	if (itModule == modules.end())
		return NULL;
	typedef IPlugin*    (__cdecl *PluginObjectGetter)();
	
	PluginObjectGetter Creater = (PluginObjectGetter)GetProcAddress(itModule->second->ModuleHANDLE, "CreatePluginObject");

	IPlugin* pluginObj = Creater == NULL ? NULL : Creater();
	if (pluginObj != NULL) {
		plugins.insert(std::make_pair(pluginObj, itModule->second));
		pluginObj->Initialize(cine);
	}

	return pluginObj;
}

bool PluginManagerBase::DestroyPluginObject( IPlugin* plugin ) {
	typedef void (__cdecl *PluginObjectDeleter)(IPlugin *);
	pluginTable::iterator itPluginObj = plugins.find(plugin);
	if (itPluginObj == plugins.end())
		return false;

	DeactivatePlugin(itPluginObj->first);

	(itPluginObj->first)->Release();

	PluginObjectDeleter Deleter = (PluginObjectDeleter) GetProcAddress(itPluginObj->second->ModuleHANDLE, "DestroyPluginObject");
	Deleter(itPluginObj->first);

	plugins.erase(itPluginObj);
	return true;
}

void PluginManagerBase::Destroy() {
	while (!modules.empty()) {
		ReleaseModule(modules.begin()->second->ModuleHANDLE);
	}
}

bool PluginManagerBase::ActivatePlugin( IPlugin * plugin ) {
	pluginTable::iterator iter = plugins.find(plugin);
	if (iter == plugins.end()) {
		WriteLog("warning : try to active an plugin that do not belong to this manager.");
		return false;
	} else {
		plugin->Activate();
		return true;
	}
}

bool PluginManagerBase::DeactivatePlugin( IPlugin * plugin ) {
	pluginTable::iterator iter = plugins.find(plugin);
	if (iter == plugins.end()) {
		WriteLog("warning : try to deactive an plugin that do not belong to this manager.");
		return false;
	} else {
		plugin->Deactivate();
		return true;
	}
}